/**
 *	@file	main.c
 *	@brief	the main function of tcpproxy, it read command line parameter
 *		then run a proxy. 
 * 
 *	@author	FZ
 */


#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include "debug.h"
#include "proxy.h"

static proxy_arg_t	_g_arg;		/* the proxy argument */

static void _usage(void)
{
	printf("tcpproxy <options> <(realsvr address(ip:port)>\n");
	printf("\t-a <IP:port>\tproxy address\n");
	printf("\t-w <N>\t\twork thread number\n");
	printf("\t-m <N>\t\tproxy max concurrency\n");
	printf("\t-b <algo>\tbind CPU, algorithm is: rr|odd|even\n");
	printf("\t-r <range>\tthe HT CPU range: full|low|high\n");
	printf("\t-d <0-4>\tthe debug level<0-4>, default is 0(disabled)\n");
	printf("\t-s\t\tuse splice\n");
	printf("\t-n\t\tuse nb_splice\n");
	printf("\t-h\t\tshow help usage\n");
}


static int _parse_cmd(int argc, char **argv)
{
	char c;
	char optstr[] = ":a:w:m:b:r:d:snh";
	int i;

	opterr = 0;
	while ( (c = getopt(argc, argv, optstr)) != -1) {
		switch (c) {

		case 'a':
			if (ip_port_from_str(&_g_arg.httpaddr, optarg)) {
				printf("invalid proxy address: %s\n", 
				       optarg);
				return -1;
			}
			break;

		case 'w':
			_g_arg.nwork = atoi(optarg);
			if (_g_arg.nwork < 1 || _g_arg.nwork > MAX_WORKNUM) {
				printf("invalid work number: %s\n", 
				       optarg);
				return -1;
			}
			break;

		case 'm':
			_g_arg.max = atoi(optarg);
			if (_g_arg.max < 1) {
				printf("invalid capacity: %s\n", optarg);
				return -1;
			}
			break;

		case 'b':
			if (strcmp(optarg, "rr") == 0)
				_g_arg.bind_cpu_algo = THREAD_BIND_RR;
			else if (strcmp(optarg, "odd") == 0)
				_g_arg.bind_cpu_algo = THREAD_BIND_ODD;
			else if (strcmp(optarg, "even") == 0)
				_g_arg.bind_cpu_algo = THREAD_BIND_EVEN;
			else {
				printf("invalid bind CPU algorithm %s\n", optarg);
				return -1;
			}
			_g_arg.bind_cpu = 1;
			break;

		case 'r':
			if (strcmp(optarg, "full") == 0)
				_g_arg.bind_cpu_ht = THREAD_HT_FULL;
			else if (strcmp(optarg, "low") == 0)
				_g_arg.bind_cpu_ht = THREAD_HT_LOW;
			else if (strcmp(optarg, "high") == 0)
				_g_arg.bind_cpu_ht = THREAD_HT_HIGH;
			else {
				printf("invalid bind CPU ht %s\n", optarg);
				return -1;
			}
			_g_arg.bind_cpu = 1;
			break;

		case 'd':
			g_dbglvl = atoi(optarg);
			if (g_dbglvl < 0 || g_dbglvl > 4) {
				printf("invalid debug level: %s\n", optarg);
				return -1;
			}
			break;

		case 's':
			_g_arg.use_splice = 1;
			break;

		case 'n':
			_g_arg.use_nb_splice = 1;
			break;

		case 'h':
			_usage();
			exit(0);

		case ':':
			printf("%c need argument\n", optopt);
			return -1;

		case '?':
			printf("unkowned option: %c\n", optopt);
			return -1;
		}
	}

	if (optind >= argc) {
		printf("no real server\n");
		return -1;
	}
	
	if (argc - optind > MAX_REALSVR) {
		printf("too many realsvrs, excceed %d\n", MAX_REALSVR);
		return -1;
	}

	for (i = optind; i < argc; i++) {
		if (ip_port_from_str(&_g_arg.rsaddrs[i-optind], argv[i])) {
			printf("invalid real server: %s\n", argv[i]);
			return -1;
		}
		_g_arg.nrsaddr++;
	}

	if (_g_arg.httpaddr.family == 0) {
		printf("no server address\n");
		return -1;
	}

	if (_g_arg.use_splice && _g_arg.use_nb_splice) {
		printf("the splice and nb_splice can set only one\n");
		return -1;
	}

	return 0;
}

int main(int argc, char **argv)
{
	if (_parse_cmd(argc, argv)) {
		_usage();
		return -1;
	}

	proxy_main(&_g_arg);

	return 0;
}




